home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / file / managers / mc-3.2 / mc-3 / mc-3.2.1 / src / file.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-17  |  54.3 KB  |  2,087 lines

  1. /* {{{ Copyright */
  2.  
  3. /* File managing
  4.    Copyright (C) 1994, 1995 The Free Software Foundation
  5.    
  6.    Written by: 1994, 1995 Janne Kukonlehto
  7.                1994, 1995 Fred Leeflang
  8.                1994, 1995 Miguel de Icaza
  9.                1995       Jakub Jelinek
  10.  
  11.    The copy code was based in GNU's cp, and was written by:
  12.    Torbjorn Granlund, David MacKenzie, and Jim Meyering.
  13.  
  14.    The move code was based in GNU's mv, and was written by:
  15.    Mike Parker and David MacKenzie.
  16.  
  17.    Janne Kukonlehto added much error recovery to them for being used
  18.    in an interactive program.
  19.    
  20.    This program is free software; you can redistribute it and/or modify
  21.    it under the terms of the GNU General Public License as published by
  22.    the Free Software Foundation; either version 2 of the License, or
  23.    (at your option) any later version.
  24.    
  25.    This program is distributed in the hope that it will be useful,
  26.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  27.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  28.    GNU General Public License for more details.
  29.  
  30.    You should have received a copy of the GNU General Public License
  31.    along with this program; if not, write to the Free Software
  32.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  33.  
  34. /* }}} */
  35.  
  36. #include <config.h>
  37. /* Hack: the vfs code should not rely on this */
  38. #define WITH_FULL_PATHS 1
  39.  
  40. /* {{{ Include files */
  41.  
  42. #include <sys/types.h>
  43. #include <dirent.h>
  44. #include <stdio.h>
  45. #include <errno.h>
  46. #include "tty.h"
  47. #include <ctype.h>
  48. #include <malloc.h>
  49. #include <string.h>
  50. #ifdef HAVE_UNISTD_H
  51. #   include <unistd.h>
  52. #endif
  53. #include <sys/stat.h>
  54. #include <sys/param.h>
  55. #include <fcntl.h>
  56. #include <time.h>
  57. #ifdef _OS_NT
  58. #include <sys/utime.h>
  59. #else
  60. #include <utime.h>
  61. #endif
  62. #include "mad.h"
  63. #include "regex.h"
  64. #include "util.h"
  65. #include "dialog.h"
  66. #include "global.h"
  67. /* Needed by query_replace */
  68. #include "color.h"
  69. #include "win.h"
  70. #include "dlg.h"
  71. #include "widget.h"
  72. #define WANT_WIDGETS
  73. #include "main.h"        /* WANT_WIDGETS-> we get the the_hint def */
  74. #include "file.h"
  75. #include "layout.h"
  76. #include "widget.h"
  77. #include "wtools.h"
  78.  
  79. /* This for the background code, we currenlty only support one
  80.  * way of comunicating the background and foreground process
  81.  * by using the socketpair system call
  82.  */
  83. #ifdef USE_NETCODE
  84. #    include <sys/socket.h>
  85. #endif
  86.  
  87. /* Needed for current_panel, other_panel and WTree */
  88. #include "dir.h"
  89. #include "panel.h"
  90. #include "tree.h"
  91.  
  92. #include "key.h"
  93. #include "../vfs/vfs.h"
  94.  
  95. /* }}} */
  96.  
  97. /* rcsid [] = "$Id: file.c,v 1.12 1995/02/21 19:05:47 miguel Exp $" */
  98. int verbose = 1;
  99.  
  100. /* This is a hard link cache */
  101. struct link {
  102.     struct link *next;
  103.     vfs *vfs;
  104.     dev_t dev;
  105.     ino_t ino;
  106.     short linkcount;
  107.     char name[1];
  108. };
  109.  
  110. struct link *linklist = NULL;
  111. struct re_pattern_buffer rx;
  112. struct re_registers regs;
  113. static char *dest_mask = NULL;
  114. static int op_follow_symlinks = 0;
  115. int dive_into_subdirs = 1;
  116. int stable_symlinks = 0;
  117. int preserve_uidgid = 1;
  118.  
  119. #define WX 60
  120. #define WY 10
  121. #define BY 10
  122.  
  123. static Dlg_head *op_dlg;
  124. static char *op_names [] = { " Copy ", " Move ", " Delete " };
  125. static int selected_button;
  126. static int last_percentage [3];
  127.  
  128. enum { REPLACE_YES = B_USER, REPLACE_NO, REPLACE_APPEND, REPLACE_ALWAYS,
  129.        REPLACE_UPDATE, REPLACE_NEVER, REPLACE_ABORT };
  130.  
  131. static int replace_colors [4];
  132. static Dlg_head *replace_dlg;
  133. static char *replace_filename;
  134. static struct stat *s_stat, *d_stat;
  135. static int replace_result;
  136.  
  137. int know_what_am_i_doing = 1;
  138.  
  139. enum { RECURSIVE_YES, RECURSIVE_NO, RECURSIVE_ALWAYS,
  140.        RECURSIVE_NEVER, RECURSIVE_ABORT };
  141.  
  142. static int recursive_result;
  143. static int recursive_erase (char *s);
  144.  
  145. static WLabel *FileLabel [2];
  146. static WLabel *FileString [2];
  147. static WLabel *ProgressLabel [3];
  148. static WGauge *ProgressGauge [3];
  149.  
  150. /* }}} */
  151.  
  152. /* {{{ File progress display routines */
  153.  
  154. static void update_buttons (void)
  155. {
  156. #ifndef HAVE_X
  157. #define h op_dlg
  158.     int minus = verbose ? 0 : 3;
  159.     
  160.     attrset ((selected_button == FILE_SKIP) ? FOCUSC : NORMALC);
  161.     dlg_move (op_dlg, BY-minus, 14);
  162.     addstr ("[ Skip ]");
  163.     attrset ((selected_button == FILE_ABORT) ? FOCUSC : NORMALC);
  164.     dlg_move (op_dlg, BY-minus, WX - 19);
  165.     addstr ("[ Abort ]");
  166.     refresh ();
  167. #undef h
  168. #endif    
  169. }
  170.  
  171. static int check_buttons (void)
  172. {
  173. #ifndef HAVE_X
  174.     int c;
  175.  
  176. #if 0
  177.     rotate_dash ();
  178. #endif
  179.  
  180.     /* no delay please says the 1 there */
  181.     c = get_key_code (1);
  182.     
  183.     if (c == ERR)
  184.         return FILE_CONT;
  185.  
  186.     switch (c){
  187.     case 's':
  188.     selected_button = FILE_SKIP;
  189.     update_buttons ();
  190.     return selected_button;
  191.     case 'a':
  192.     case KEY_F(10):
  193.     case XCTRL('g'):
  194.     case XCTRL('c'):
  195.     case ESC_CHAR:
  196.     selected_button = FILE_ABORT;
  197.     update_buttons ();
  198.     return selected_button;
  199.     case ' ':
  200.     case '\n':
  201.     case KEY_ENTER:
  202.     return selected_button;
  203.     case '\t':
  204.     case KEY_LEFT:
  205.     case KEY_RIGHT:
  206.     selected_button = (selected_button == FILE_SKIP) ? FILE_ABORT : FILE_SKIP;
  207.     update_buttons ();
  208.     /* Fall through */
  209.     default:
  210.     return FILE_CONT;
  211.     }
  212. #else
  213. #ifdef HAVE_TK
  214.     tk_dispatch_all ();
  215. #endif
  216. #ifdef HAVE_XVIEW
  217.     xv_dispatch_something ();
  218. #endif
  219.     if (op_dlg->running)
  220.         return FILE_CONT;
  221. #    ifdef ZERO
  222. /*FIXME: I think FRAME_CMD kills the dialog whenever we click a button, so that is
  223.     why this is ifdefed*/
  224.     else if (op_dlg->ret_value == B_CANCEL)
  225.         return FILE_ABORT;
  226.     else
  227.         return op_dlg->ret_value;
  228. #    else
  229.     return FILE_ABORT;
  230. #    endif        
  231. #endif
  232. }
  233.  
  234. static int op_win_callback (struct Dlg_head *h, int id, int msg)
  235. {
  236.     switch (msg){
  237. #ifndef HAVE_X    
  238.     case DLG_DRAW:
  239.     attrset (REVERSE_COLOR);
  240.     dlg_erase (h);
  241.     draw_box (h, 1, 2, h->lines-2, h->cols-4);
  242.     update_buttons ();
  243.     return 1;
  244. #endif
  245.     }
  246.     return 0;
  247. }
  248.  
  249. /* Used to save the hint line */
  250. static int last_hint_line;
  251.  
  252. void create_op_win (int op)
  253. {
  254.     int i;
  255. #ifdef HAVE_XVIEW    
  256.     char *sixty = "                                                                                   ";
  257.     char *fifteen = "               ";
  258. #else
  259.     char *sixty = "";
  260.     char *fifteen = "";
  261. #endif    
  262.  
  263.     replace_result = 0;
  264.     recursive_result = 0;
  265.  
  266.     op_dlg = create_dlg (0, 0, WY+4, WX+4, dialog_colors,
  267.              op_win_callback, "", "opwin", DLG_CENTER);
  268.  
  269.     last_hint_line = the_hint->widget.y;
  270.     if ((op_dlg->y + op_dlg->lines) > last_hint_line)
  271.     the_hint->widget.y = op_dlg->y + op_dlg->lines+1;
  272.     
  273.     x_set_dialog_title (op_dlg, "");
  274.  
  275. #ifndef HAVE_X
  276.     add_widget (op_dlg, button_new (0, 0, 0, "", 0, -1, 0, 0));
  277. #else
  278.     tk_new_frame (op_dlg, "b.");
  279.     add_widgetl (op_dlg, button_new (0, 0, FILE_ABORT, "Abort", 0, -1, 0, 0),
  280.         XV_WLAY_RIGHTOF);
  281.     add_widgetl (op_dlg, button_new (0, 0, FILE_SKIP, "Skip", 0, -1, 0, 0),
  282.         XV_WLAY_CENTERROW);
  283. #endif        
  284.     tk_new_frame (op_dlg, "2.");
  285.     add_widgetl (op_dlg, ProgressGauge [2] = gauge_new (8, 14, 0, 100, 0), 
  286.         XV_WLAY_RIGHTOF);
  287.     add_widgetl (op_dlg, ProgressLabel [2] = label_new (8, 5, fifteen), 
  288.         XV_WLAY_NEXTROW);
  289.     tk_new_frame (op_dlg, "1.");
  290.     add_widgetl (op_dlg, ProgressGauge [1] = gauge_new (7, 14, 0, 100, 0), 
  291.         XV_WLAY_RIGHTOF);
  292.     add_widgetl (op_dlg, ProgressLabel [1] = label_new (7, 5, fifteen), 
  293.         XV_WLAY_NEXTROW);
  294.     tk_new_frame (op_dlg, "0.");
  295.     add_widgetl (op_dlg, ProgressGauge [0] = gauge_new (6, 14, 0, 100, 0), 
  296.         XV_WLAY_RIGHTOF);
  297.     add_widgetl (op_dlg, ProgressLabel [0] = label_new (6, 5, fifteen), 
  298.         XV_WLAY_NEXTROW);
  299.     tk_new_frame (op_dlg, "f1.");
  300.     add_widgetl (op_dlg, FileString [1] = label_new (4, 14, sixty),
  301.         XV_WLAY_RIGHTOF);
  302.     add_widgetl (op_dlg, FileLabel [1] = label_new (4, 5, fifteen), 
  303.         XV_WLAY_NEXTROW);
  304.     tk_new_frame (op_dlg, "f0.");
  305.     add_widgetl (op_dlg, FileString [0] = label_new (3, 14, sixty),
  306.         XV_WLAY_RIGHTOF);
  307.     add_widgetl (op_dlg, FileLabel [0] = label_new (3, 5, fifteen), 
  308.         XV_WLAY_NEXTROW);
  309.     
  310.     /* We will manage the dialog without any help, that's why
  311.        we have to call init_dlg */
  312.     init_dlg (op_dlg);
  313.     op_dlg->running = 1;
  314.     selected_button = FILE_SKIP;
  315.     update_buttons ();
  316.     for (i = 0; i < 3; i++)
  317.     last_percentage [i] = -99;
  318. }
  319.  
  320. void destroy_op_win (void)
  321. {
  322. #ifdef HAVE_XVIEW
  323.     xtoolkit_kill_dialog (op_dlg);
  324. #endif
  325.     dlg_run_done (op_dlg);
  326.     destroy_dlg (op_dlg);
  327.     the_hint->widget.y = last_hint_line;
  328. }
  329.  
  330. static int show_no_bar (int n)
  331. {
  332.     if (n >= 0) {
  333.         label_set_text (ProgressLabel [n], "");
  334.         gauge_show (ProgressGauge [n], 0);
  335.     }
  336.     return check_buttons ();
  337. }
  338.  
  339. #ifndef HAVE_X
  340. #define truncFileString(s) name_trunc (s, 47)
  341. #else
  342. #define truncFileString(s) s
  343. #endif
  344.  
  345. static int show_source (char *s)
  346. {
  347.     if (s != NULL){
  348.  
  349. #ifdef WITH_FULL_PATHS
  350.         int i = strlen (cpanel->cwd);
  351.  
  352.     /* We remove the full path we have added before */
  353.         if (!strncmp (s, cpanel->cwd, i)){ 
  354.             if (s[i] == PATH_SEP)
  355.                 s += i + 1;
  356.         }
  357. #endif /* WITH_FULL_PATHS */
  358.     
  359.     label_set_text (FileLabel [0], "Source");
  360.     label_set_text (FileString [0], truncFileString (s));
  361.     return check_buttons ();
  362.     } else {
  363.     label_set_text (FileLabel [0], "");
  364.     label_set_text (FileString [0], "");
  365.     return check_buttons ();
  366.     }
  367. }
  368.  
  369. static int show_target (char *s)
  370. {
  371.     if (s != NULL){
  372.     label_set_text (FileLabel [1], "Target");
  373.     label_set_text (FileString [1], truncFileString (s));
  374.     return check_buttons ();
  375.     } else {
  376.     label_set_text (FileLabel [1], "");
  377.     label_set_text (FileString [1], "");
  378.     return check_buttons ();
  379.     }
  380. }
  381.  
  382. static int show_deleting (char *s)
  383. {
  384.     label_set_text (FileLabel [0], "Deleting");
  385.     label_set_text (FileString [0], truncFileString (s));
  386.     return check_buttons ();
  387. }
  388.  
  389. static int show_bar (int n, long done, long total)
  390. {
  391.     gauge_set_value (ProgressGauge [n], (int) total, (int) done);
  392.     gauge_show (ProgressGauge [n], 1);
  393.     return check_buttons ();
  394. }
  395.  
  396. static int show_file_progress (long done, long total)
  397. {
  398.     if (!verbose)
  399.     return check_buttons ();
  400.     if (total > 0){
  401.     label_set_text (ProgressLabel [0], "File");
  402.     return show_bar (0, done, total);
  403.     } else
  404.     return show_no_bar (0);
  405. }
  406.  
  407. static int show_count_progress (long done, long total)
  408. {
  409.     if (!verbose)
  410.     return check_buttons ();
  411.     if (total > 0){
  412.     label_set_text (ProgressLabel [1], "Count");
  413.     return show_bar (1, done, total);
  414.     } else
  415.     return show_no_bar (1);
  416. }
  417.  
  418. static int show_bytes_progress (long done, long total)
  419. {
  420.     if (!verbose)
  421.     return check_buttons ();
  422.     if (total > 0){
  423.     label_set_text (ProgressLabel [2], "Bytes");
  424.     return show_bar (2, done, total);
  425.     } else
  426.     return show_no_bar (2);
  427. }
  428.  
  429. /* }}} */
  430.  
  431. /* {{{ Query/status report routines */
  432.  
  433. static int do_file_error (char *error)
  434. {
  435.     int result;
  436.  
  437.     result = query_dialog (" Error ", error, 3, 3, " Skip ", " Retry ", " Abort ");
  438.  
  439.     switch (result){
  440.     case 0:
  441.     do_refresh ();
  442.     return FILE_SKIP;
  443.     case 1:
  444.     do_refresh ();
  445.     return FILE_RETRY;
  446.     case 2:
  447.     default:
  448.     return FILE_ABORT;
  449.     }
  450. }
  451.  
  452. /* Report error with one file */
  453. static int file_error (char *format, char *file)
  454. {
  455.     sprintf (cmd_buf, format,
  456.          name_trunc (file, 30), unix_error_string (errno));
  457.     return do_file_error (cmd_buf);
  458. }
  459.  
  460. /* Report error with two files */
  461. static int files_error (char *format, char *file1, char *file2)
  462. {
  463.     sprintf (cmd_buf, format, name_trunc (file1, 15),
  464.          name_trunc (file2, 15), unix_error_string (errno));
  465.     return do_file_error (cmd_buf);
  466. }
  467.  
  468. static char *format = "Target file \"%s\" already exists!";
  469. static int replace_callback (struct Dlg_head *h, int Id, int Msg)
  470. {
  471. #ifndef HAVE_X
  472.  
  473.     switch (Msg){
  474.     case DLG_DRAW:
  475.     attrset (ERROR_COLOR);
  476.     dlg_erase (h);
  477.     draw_box (h, 1, 2, 12, 56);
  478.  
  479.     dlg_move (h, 1, 24);
  480.     addstr (" File exists ");
  481.     break;
  482.     }
  483. #endif
  484.     return 0;
  485. }
  486.  
  487. static void init_replace (void)
  488. {
  489.     char buffer [128];
  490.     
  491.     replace_colors [0] = ERROR_COLOR;
  492.     replace_colors [1] = REVERSE_COLOR;
  493.     replace_colors [2] = ERROR_COLOR;
  494.     replace_colors [3] = REVERSE_COLOR;
  495.     
  496.     replace_dlg = create_dlg (0, 0, 14, 60, replace_colors, replace_callback,
  497.                   "[Replace]", "replace", DLG_CENTER);
  498.                   
  499.     x_set_dialog_title (replace_dlg, "File exists");
  500.  
  501. #ifdef HAVE_X
  502. #define X_TRUNC 128
  503. #else
  504. #define X_TRUNC 52
  505. #endif
  506.     sprintf (buffer, format, name_trunc (replace_filename, X_TRUNC - strlen (format)));
  507.     add_widgetl (replace_dlg, label_new (3, 5, buffer), XV_WLAY_CENTERROW);
  508.     
  509.     add_widgetl (replace_dlg,
  510.         button_new (BY + 1, 46, REPLACE_ABORT, "[ Abort ]", 'a', 2, 0, 0),
  511.         XV_WLAY_CENTERROW);
  512.     
  513.     tk_new_frame (replace_dlg, "a.");
  514.     add_widgetl (replace_dlg,
  515.         button_new (BY - 1, 47, REPLACE_NEVER, "[ nonE ]", 'e', 5, 0, 0),
  516.         XV_WLAY_RIGHTOF);
  517.     add_widgetl (replace_dlg,
  518.         button_new (BY - 1, 36, REPLACE_UPDATE, "[ Update ]", 'u', 2, 0, 0),
  519.         XV_WLAY_RIGHTOF);
  520.     add_widgetl (replace_dlg,
  521.         button_new (BY - 1, 28, REPLACE_ALWAYS, "[ alL ]", 'l', 4, 0, 0),
  522.         XV_WLAY_RIGHTOF);
  523.  
  524.     add_widgetl (replace_dlg, label_new (BY-1, 5, "Overwrite all targets?"),
  525.             XV_WLAY_CENTERROW);        
  526.  
  527.     /* "this target..." widgets */
  528.     tk_new_frame (replace_dlg, "p.");
  529.     if (!S_ISDIR (d_stat->st_mode))
  530.         add_widgetl (replace_dlg,
  531.             button_new (BY - 2, 43, REPLACE_APPEND, "[ apPend ]", 'p', 2, 0, 0),
  532.             XV_WLAY_RIGHTOF);
  533.     add_widgetl (replace_dlg,
  534.         button_new (BY - 2, 36, REPLACE_NO, "[ No ]", 'n', 2, 0, 0),
  535.         XV_WLAY_RIGHTOF);
  536.     add_widgetl (replace_dlg,
  537.         button_new (BY - 2, 28, REPLACE_YES, "[ Yes ]", 'y', 2, 0, 0), 
  538.         XV_WLAY_RIGHTOF);
  539.     add_widgetl (replace_dlg, label_new (BY-2,5, "Overwrite this target?"),
  540.          XV_WLAY_CENTERROW);
  541.     
  542.     tk_new_frame (replace_dlg, "i.");
  543.     sprintf (buffer, "Target date: %s, size %d",
  544.          file_date (d_stat->st_mtime), (int) d_stat->st_size);
  545.     add_widgetl (replace_dlg, label_new (6, 5, buffer), XV_WLAY_CENTERROW);
  546.     sprintf (buffer, "Source date: %s, size %d",
  547.          file_date (s_stat->st_mtime), (int) s_stat->st_size);
  548.     add_widgetl (replace_dlg, label_new (5, 5, buffer), XV_WLAY_CENTERROW);
  549.     tk_end_frame ();
  550. }
  551.  
  552. static int do_append = 0;
  553.  
  554. static int query_replace (char *destname, struct stat *_s_stat,
  555.               struct stat *_d_stat)
  556. {
  557.     if (replace_result < REPLACE_ALWAYS){
  558.     replace_filename = destname;
  559.     s_stat = _s_stat;
  560.     d_stat = _d_stat;
  561.     init_replace ();
  562.     run_dlg (replace_dlg);
  563.     replace_result = replace_dlg->ret_value;
  564.     if (replace_result == B_CANCEL)
  565.         replace_result = REPLACE_ABORT;
  566.     destroy_dlg (replace_dlg);
  567.     }
  568.  
  569.     switch (replace_result){
  570.     case REPLACE_UPDATE:
  571.     do_refresh ();
  572.     if (s_stat->st_mtime > d_stat->st_mtime)
  573.         return FILE_CONT;
  574.     else
  575.         return FILE_SKIP;
  576.     
  577.     case REPLACE_APPEND:
  578.         do_append = 1;
  579.     case REPLACE_YES:
  580.     case REPLACE_ALWAYS:
  581.     do_refresh ();
  582.     return FILE_CONT;
  583.     case REPLACE_NO:
  584.     case REPLACE_NEVER:
  585.     do_refresh ();
  586.     return FILE_SKIP;
  587.     case REPLACE_ABORT:
  588.     default:
  589.     return FILE_ABORT;
  590.     }
  591. }
  592.  
  593. int query_recursive (char *s)
  594. {
  595.     char *confirm, *text;
  596.  
  597.     if (recursive_result < RECURSIVE_ALWAYS){
  598.     text = copy_strings (" Delete: ", name_trunc (s, 30), " ", 0);
  599.  
  600.         if (know_what_am_i_doing){
  601.         query_set_sel (1);
  602.         recursive_result = query_dialog (text, "\n   Directory not empty.   "
  603.                          "\n   Delete it recursively? ",
  604.                          3 | WITH_HOTKEYS, 5,
  605.                          "y Yes ", "n No ", "l alL ", "e nonE ", "a Abort ");
  606.     } else
  607.         recursive_result = query_dialog (text, "\n   Directory not empty.   "
  608.                          "\n   Delete it recursively? ",
  609.                          3 | WITH_HOTKEYS, 5, 
  610.                          "y Yes ", "n No ", "l alL ", "e nonE ", "a Abort ");
  611.     if (recursive_result != RECURSIVE_ABORT)
  612.         do_refresh ();
  613.     free (text);
  614.     if (!know_what_am_i_doing && (recursive_result == RECURSIVE_YES
  615.         || recursive_result == RECURSIVE_ALWAYS)){
  616.         text = copy_strings (" Please type 'yes' to confirm recursive delete of ",
  617.                  recursive_result == RECURSIVE_YES
  618.                  ? name_trunc (s, 20) : "all the directories", " ", 0);
  619.         confirm = input_dialog (" Recursive Delete ",
  620.                     text, "no");
  621.         do_refresh ();
  622.         if (!confirm || strcmp (confirm, "yes"))
  623.         recursive_result = RECURSIVE_NEVER;
  624.         free (confirm);
  625.         free (text);
  626.     }
  627.     }
  628.     switch (recursive_result){
  629.     case RECURSIVE_YES:
  630.     case RECURSIVE_ALWAYS:
  631.     return FILE_CONT;
  632.     case RECURSIVE_NO:
  633.     case RECURSIVE_NEVER:
  634.     return FILE_SKIP;
  635.     case RECURSIVE_ABORT:
  636.     default:
  637.     return FILE_ABORT;
  638.     }
  639. }
  640.  
  641. /* }}} */
  642.  
  643. /* {{{ Copy routines */
  644.  
  645. enum CaseConvs { NO_CONV=0, UP_CHAR=1, LOW_CHAR=2, UP_SECT=4, LOW_SECT=8 };
  646.  
  647. int convert_case (int c, enum CaseConvs *conversion)
  648. {
  649.     if (*conversion & UP_CHAR){
  650.     *conversion &= ~UP_CHAR;
  651.     return toupper (c);
  652.     } else if (*conversion & LOW_CHAR){
  653.     *conversion &= ~LOW_CHAR;
  654.     return tolower (c);
  655.     } else if (*conversion & UP_SECT){
  656.     return toupper (c);
  657.     } else if (*conversion & LOW_SECT){
  658.     return tolower (c);
  659.     } else
  660.     return c;
  661. }
  662.  
  663. static int transform_error = 0;
  664. static char *transform_source (char *source)
  665. {
  666.     int j, k, l, len;
  667.     char *fnsource = x_basename (source);
  668.     int next_reg;
  669.     enum CaseConvs case_conv = NO_CONV;
  670.     static char fntarget [MC_MAXPATHLEN];
  671.     
  672.     len = strlen (fnsource);
  673.     j = re_match (&rx, fnsource, len, 0, ®s);
  674.     if (j != len) {
  675.         transform_error = FILE_SKIP;
  676.         return NULL;
  677.     }
  678.     for (next_reg = 1, j = 0, k = 0; j < strlen (dest_mask); j++) {
  679.         switch (dest_mask [j]) {
  680.     case '\\':
  681.         j++;
  682.         if (! isdigit (dest_mask [j])){
  683.         /* Backslash followed by non-digit */
  684.         switch (dest_mask [j]){
  685.         case 'U':
  686.             case_conv |= UP_SECT;
  687.             case_conv &= ~LOW_SECT;
  688.             break;
  689.         case 'u':
  690.             case_conv |= UP_CHAR;
  691.             break;
  692.         case 'L':
  693.             case_conv |= LOW_SECT;
  694.             case_conv &= ~UP_SECT;
  695.             break;
  696.         case 'l':
  697.             case_conv |= LOW_CHAR;
  698.             break;
  699.         case 'E':
  700.             case_conv = NO_CONV;
  701.             break;
  702.         default:
  703.             /* Backslash as quote mark */
  704.             fntarget [k++] = convert_case (dest_mask [j], &case_conv);
  705.         }
  706.         break;
  707.         } else {
  708.         /* Backslash followed by digit */
  709.         next_reg = dest_mask [j] - '0';
  710.         /* Fall through */
  711.         }
  712.                 
  713.     case '*':
  714.         if (next_reg < 0 || next_reg >= RE_NREGS
  715.         || regs.start [next_reg] < 0) {
  716.         message (1, " Error ", " Invalid target mask ");
  717.         transform_error = FILE_ABORT;
  718.         return NULL;
  719.         }
  720.         for (l = regs.start [next_reg]; l < regs.end [next_reg]; l++)
  721.         fntarget [k++] = convert_case (fnsource [l], &case_conv);
  722.         next_reg ++;
  723.         break;
  724.                 
  725.     default:
  726.         fntarget [k++] = convert_case (dest_mask [j], &case_conv);
  727.         break;
  728.         }
  729.     }
  730.     fntarget [k] = 0;
  731.     return fntarget;
  732. }
  733.  
  734. void free_linklist (void)
  735. {
  736.     struct link *lp, *lp2;
  737.     
  738.     for (lp = linklist; lp != NULL; lp = lp2){
  739.         lp2 = lp -> next;
  740.         free (lp);
  741.     }
  742.     linklist = NULL;
  743. }
  744.  
  745. /* Returns 0 if the inode wasn't found in the cache and 1 if it was found
  746.    and a hardlink was succesfully made */
  747. int check_hardlinks (char *src_name, char *dst_name, struct stat *pstat)
  748. {
  749.     struct link *lp;
  750.     vfs *my_vfs = vfs_type (src_name);
  751.     ino_t ino = pstat->st_ino;
  752.     dev_t dev = pstat->st_dev;
  753.     struct stat link_stat;
  754.     char *p;
  755.  
  756.     if (vfs_file_is_ftp (src_name))
  757.         return 0;
  758.     for (lp = linklist; lp != NULL; lp = lp -> next)
  759.         if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev){
  760.             if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino &&
  761.                 link_stat.st_dev == dev && vfs_type (lp->name) == my_vfs){
  762.                 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
  763.                                      was copied to */
  764.                 if (vfs_type (dst_name) == vfs_type (p)){
  765.                     if (!mc_stat (p, &link_stat)){
  766.                         if (!mc_link (p, dst_name))
  767.                             return 1;
  768.                     }
  769.                 }
  770.             }
  771.             /* FIXME: Announce we couldn't make the hardlink */
  772.             return 0;
  773.         }
  774.     lp = (struct link *) xmalloc (sizeof (struct link) + strlen (src_name) 
  775.                                   + strlen (dst_name) + 1, "Hardlink cache");
  776.     if (lp){
  777.         lp->vfs = my_vfs;
  778.         lp->ino = ino;
  779.         lp->dev = dev;
  780.         strcpy (lp->name, src_name);
  781.         p = strchr (lp->name, 0) + 1;
  782.         strcpy (p, dst_name);
  783.         lp->next = linklist;
  784.         linklist = lp;
  785.     }
  786.     return 0;
  787. }
  788.  
  789. int copy_file_file (char *src_path, char *dst_path, int toplevel, 
  790.     int ask_overwrite)
  791. {
  792. #ifndef _OS_NT
  793.     uid_t src_uid;
  794.     gid_t src_gid;
  795. #endif
  796.     char *buf = 0;
  797.     int  buf_size = 8*1024;
  798.     int  dest_desc = 0;
  799.     int  source_desc;
  800.     int  n_read;
  801.     int  n_written;
  802.     int  src_mode;        /* The mode of the source file */
  803.     struct stat sb, sb2;
  804.     struct utimbuf utb;
  805.     int  dst_exists = 0;
  806.     long n_read_total = 0;
  807.     long file_size;
  808.     int  return_status, temp_status;
  809.     int do_remote_copy = 0;
  810.     int appending = 0;
  811.  
  812.     return_status = FILE_RETRY;
  813.  
  814.     if (show_source (src_path) == FILE_ABORT
  815.     || show_target (dst_path) == FILE_ABORT)
  816.     return FILE_ABORT;
  817.     refresh ();
  818.  
  819.  retry_dst_stat:
  820.     if (!mc_lstat (dst_path, &sb2)){
  821.     if (S_ISDIR (sb2.st_mode)){
  822.         return_status = file_error (" Cannot overwrite directory \"%s\" ",
  823.                     dst_path);
  824.         if (return_status == FILE_RETRY)
  825.         goto retry_dst_stat;
  826.         return return_status;
  827.     }
  828.     dst_exists = 1;
  829.     }
  830.  
  831.  retry_src_lstat:
  832.     if (mc_lstat (src_path, &sb)){
  833.     return_status = file_error (" Cannot lstat source file \"%s\" \n %s ",
  834.                     src_path);
  835.     if (return_status == FILE_RETRY)
  836.         goto retry_src_lstat;
  837.     return return_status;
  838.     }
  839.     
  840.     if (dst_exists){
  841.     /* Destination already exists */
  842.     if (sb.st_dev == sb2.st_dev
  843.         && sb.st_ino == sb2.st_ino){
  844.         message (1, " Error ", " `%s' and `%s' are the same file. ",
  845.              src_path, dst_path);
  846.         do_refresh ();
  847.         return FILE_SKIP;
  848.     }
  849.     if (S_ISDIR (sb2.st_mode)){
  850.         message (1, " Error ", " Cannot overwrite directory `%s' ",
  851.              dst_path);
  852.         do_refresh ();
  853.         return FILE_SKIP;
  854.     }
  855.  
  856.     /* Should we replace destination? */
  857.     if (ask_overwrite) {
  858.         return_status = query_replace (dst_path, &sb, &sb2);
  859.         if (return_status != FILE_CONT)
  860.             return return_status;
  861.     }
  862.     }
  863.  
  864.     if (!do_append) {
  865.     
  866.         /* Check the hardlinks */
  867.         if (sb.st_nlink > 1 && check_hardlinks (src_path, dst_path, &sb) == 1) {
  868.             /* We have made a hardlink - no more processing is necessary */
  869.             return return_status;
  870.         }
  871. #ifndef _OS_NT    
  872.         if (S_ISLNK (sb.st_mode) && (!toplevel || !op_follow_symlinks)){
  873.         char link_target [MC_MAXPATHLEN];
  874.         int len;
  875.  
  876.         retry_src_readlink:
  877.         len = mc_readlink (src_path, link_target, MC_MAXPATHLEN);
  878.         if (len < 0){
  879.             return_status = file_error
  880.             (" Cannot read source link \"%s\" \n %s ", src_path);
  881.             if (return_status == FILE_RETRY)
  882.             goto retry_src_readlink;
  883.             return return_status;
  884.         }
  885.         link_target [len] = 0;
  886.         if (stable_symlinks && *link_target != PATH_SEP) {
  887.         char *p, *q, *r, *s;
  888.         
  889.         p = strdup (src_path);
  890.         r = strrchr (p, PATH_SEP);
  891.         if (r) {
  892.             r[1] = 0;
  893.             if (*dst_path == PATH_SEP)
  894.                 q = strdup (dst_path);
  895.             else
  896.                 q = copy_strings (p, dst_path, 0);
  897.             r = strrchr (q, PATH_SEP);
  898.             if (r) {
  899.                 r[1] = 0;
  900.                 s = copy_strings (p, link_target, NULL);
  901.                 strcpy (link_target, s);
  902.                 free (s);
  903.             s = diff_two_paths (q, link_target);
  904.             if (s) {
  905.                 strcpy (link_target, s);
  906.                 free (s);
  907.             }
  908.             }
  909.             free (q);
  910.         }
  911.         free (p);
  912.         }
  913.         
  914.         retry_dst_symlink:
  915.         if (mc_symlink (link_target, dst_path) == 0)
  916.             /* Success */
  917.             return FILE_CONT;
  918.         /*
  919.          * if dst_exists, it is obvious that this had failed.
  920.          * We can delete the old symlink and try again...
  921.          */
  922.         if (dst_exists && S_ISLNK (sb2.st_mode)) {
  923.             if (!mc_unlink (dst_path))
  924.                 if (mc_symlink (link_target, dst_path) == 0)
  925.                     /* Success */
  926.                     return FILE_CONT;
  927.         } 
  928.         return_status = file_error
  929.         (" Cannot create target symlink \"%s\" \n %s ", dst_path);
  930.         if (return_status == FILE_RETRY)
  931.         goto retry_dst_symlink;
  932.         return return_status;
  933.         }
  934. #endif /* !OS_NT */
  935.         if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) || S_ISFIFO (sb.st_mode)
  936.             || S_ISSOCK (sb.st_mode)){
  937.         retry_mknod:        
  938.             if (mc_mknod (dst_path, sb.st_mode, sb.st_rdev) < 0){
  939.             return_status = file_error
  940.             (" Cannot create special file \"%s\" \n %s ", dst_path);
  941.             if (return_status == FILE_RETRY)
  942.             goto retry_mknod;
  943.             return return_status;
  944.         }
  945.         /* Success */
  946.         return FILE_CONT;
  947.         }
  948.     }
  949.     
  950.     if (!do_append && !vfs_file_is_local (src_path) && vfs_file_is_local (dst_path)){
  951.     
  952.     mc_setctl (src_path, MCCTL_SETREMOTECOPY, dst_path);
  953.     }
  954.  retry_src_open:
  955.     if ((source_desc = mc_open (src_path, O_RDONLY)) < 0){
  956.     return_status = file_error
  957.         (" Cannot open source file \"%s\" \n %s ", src_path);
  958.     if (return_status == FILE_RETRY)
  959.         goto retry_src_open;
  960.     do_append = 0;
  961.     return return_status;
  962.     }
  963. #define USE_VFS_CTLS 1
  964. #ifdef USE_VFS_CTLS
  965.     /* Before we can use the mc_ctl routine, the ftpfs code should be
  966.      * changed so that it aborts connections as it should
  967.      *
  968.      * Jakub?
  969.      */
  970.     do_remote_copy = mc_ctl (source_desc, MCCTL_ISREMOTECOPY, 0);
  971. #else
  972. #define do_remote_copy 0
  973. #endif
  974.     
  975.     if (!do_remote_copy) {
  976.  retry_src_fstat:
  977.         if (mc_fstat (source_desc, &sb)){
  978.         return_status = file_error
  979.             (" Cannot fstat source file \"%s\" \n %s ", src_path);
  980.         if (return_status == FILE_RETRY)
  981.             goto retry_src_fstat;
  982.         do_append = 0;
  983.             goto ret;
  984.         }
  985.     } else {
  986.  retry_src_rstat:
  987.         if (mc_stat (src_path, &sb)){
  988.         return_status = file_error
  989.             (" Cannot stat source file \"%s\" \n %s ", src_path);
  990.         if (return_status == FILE_RETRY)
  991.             goto retry_src_rstat;
  992.         do_append = 0;
  993.             goto ret;
  994.         }
  995.     }
  996.     src_mode = sb.st_mode;
  997. #ifndef _OS_NT
  998.     src_uid = sb.st_uid;
  999.     src_gid = sb.st_gid;
  1000.     utb.actime = sb.st_atime;
  1001.     utb.modtime = sb.st_mtime;
  1002. #endif
  1003.     file_size = sb.st_size;
  1004.     
  1005.     /* If the target is a symlink and we do not want to append, we should
  1006.        unlink it first... */
  1007.        
  1008.     if (dst_exists && !do_append && S_ISLNK (sb2.st_mode))
  1009.         mc_unlink (dst_path);
  1010.  
  1011.     /* Create the new regular file with small permissions initially,
  1012.        to not create a security hole.  */
  1013.  
  1014.     if (!do_remote_copy) {
  1015.  retry_dst_open:
  1016.         if ((do_append && 
  1017.             (dest_desc = mc_open (dst_path, O_WRONLY | O_APPEND)) < 0) ||
  1018.             (!do_append &&
  1019.             (dest_desc = mc_open (dst_path, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)) {
  1020.         return_status = file_error
  1021.             (" Cannot create target file \"%s\" \n %s ", dst_path);
  1022.         if (return_status == FILE_RETRY)
  1023.             goto retry_dst_open;
  1024.         do_append = 0;
  1025.         goto ret2;
  1026.         }
  1027.     }
  1028.     appending = do_append;
  1029.     do_append = 0;
  1030.  
  1031.     if (!do_remote_copy) {
  1032.  retry_dst_fstat:
  1033.         /* Find out the optimal buffer size.  */
  1034.         if (mc_fstat (dest_desc, &sb)){
  1035.         return_status = file_error
  1036.             (" Cannot fstat target file \"%s\" \n %s ", dst_path);
  1037.         if (return_status == FILE_RETRY)
  1038.             goto retry_dst_fstat;
  1039.         goto ret2;
  1040.         }
  1041.         buf_size = 8*1024;
  1042.  
  1043.         buf = (char *) xmalloc (buf_size, "copy_file_file");
  1044.     }
  1045.  
  1046.     return_status = show_file_progress (0, file_size);
  1047.     refresh ();
  1048.     if (return_status != FILE_CONT)
  1049.     goto ret3;
  1050.  
  1051.     if (!do_remote_copy)
  1052.         for (;;){
  1053.     retry_src_read:
  1054.         n_read = mc_read (source_desc, buf, buf_size);
  1055.         if (n_read < 0){
  1056.             return_status = file_error
  1057.             (" Cannot read source file \"%s\" \n %s ", src_path);
  1058.             if (return_status == FILE_RETRY)
  1059.             goto retry_src_read;
  1060.             goto ret3;
  1061.         }
  1062.         if (n_read == 0)
  1063.             break;
  1064.  
  1065.         n_read_total += n_read;
  1066.  
  1067.     retry_dst_write:
  1068.         n_written = mc_write (dest_desc, buf, n_read);
  1069.         if (n_written < n_read){
  1070.             return_status = file_error
  1071.             (" Cannot write target file \"%s\" \n %s ", dst_path);
  1072.             if (return_status == FILE_RETRY)
  1073.             goto retry_dst_write;
  1074.             goto ret3;
  1075.         }
  1076.         return_status = show_file_progress (n_read_total, file_size);
  1077.         refresh ();
  1078.         if (return_status != FILE_CONT)
  1079.             goto ret3;
  1080.         }
  1081.     else {
  1082.         int i, size;
  1083.     
  1084.         for (i = 1; i;) {
  1085.             switch (size = mc_ctl (source_desc, MCCTL_REMOTECOPYCHUNK, 8192)) {
  1086.                 case MCERR_TARGETOPEN:
  1087.                     break;
  1088.                 case MCERR_READ:
  1089.                     break;
  1090.                 case MCERR_WRITE:
  1091.                     break;
  1092.             case MCERR_DATA_ON_STDIN:
  1093.             break;
  1094.                 case MCERR_FINISH:
  1095.                     i = 0;
  1096.                     break;
  1097.             }
  1098.         if (size == MCERR_TARGETOPEN){
  1099.         message (1, " Error ", " Can't open target file ");
  1100.         goto ret3;
  1101.         }
  1102.         if (size == MCERR_READ)
  1103.         goto ret3;
  1104.  
  1105.         if (i && size != MCERR_DATA_ON_STDIN){
  1106.         n_read_total += size;
  1107.  
  1108.         /* Windows NT ftp servers report that files have no
  1109.          * permissions: -------, so if we happen to have actually
  1110.          * read something, we should fix the permissions.
  1111.          */
  1112.         if (!(src_mode &
  1113.               ((S_IRUSR|S_IWUSR|S_IXUSR)    /* user */
  1114.                |(S_IXOTH|S_IWOTH|S_IROTH)  /* other */
  1115.                |(S_IXGRP|S_IWGRP|S_IRGRP)))) /* group */
  1116.             src_mode = S_IRUSR|S_IWUSR|S_IROTH|S_IRGRP;
  1117.         }
  1118.         return_status = show_file_progress (n_read_total, file_size);
  1119.         refresh ();
  1120.         if (return_status != FILE_CONT)
  1121.             goto ret3;
  1122.         }
  1123.     }
  1124.  
  1125.  retry_dst_chmod:
  1126.     if (!appending && mc_chmod (dst_path, src_mode)){
  1127.     temp_status = file_error
  1128.         (" Cannot chmod target file \"%s\" \n %s ", dst_path);
  1129.     if (temp_status == FILE_RETRY)
  1130.         goto retry_dst_chmod;
  1131.     return_status = temp_status;
  1132.     }
  1133. #ifndef _OS_NT 
  1134.     if (!appending && preserve_uidgid) {
  1135.      retry_dst_chown:
  1136.         if (mc_chown (dst_path, src_uid, src_gid)){
  1137.         temp_status = file_error
  1138.             (" Cannot chown target file \"%s\" \n %s ", dst_path);
  1139.         if (temp_status == FILE_RETRY)
  1140.             goto retry_dst_chown;
  1141.         return_status = temp_status;
  1142.         }
  1143.     }
  1144. #endif
  1145.  ret:
  1146.     if (buf)
  1147.         free (buf);
  1148.  ret0:        
  1149.  retry_dst_close:
  1150.     if (!do_remote_copy) {
  1151.         if (mc_close (dest_desc) < 0){
  1152.         temp_status = file_error
  1153.             (" Cannot close target file \"%s\" \n %s ", dst_path);
  1154.         if (temp_status == FILE_RETRY)
  1155.             goto retry_dst_close;
  1156.         return_status = temp_status;
  1157.         }
  1158.     }
  1159.     if (!appending)
  1160.         utime (dst_path, &utb);
  1161.  
  1162.  ret2:
  1163.  retry_src_close:
  1164.     if (mc_close (source_desc) < 0){
  1165.     temp_status = file_error
  1166.         (" Cannot close source file \"%s\" \n %s ", src_path);
  1167.     if (temp_status == FILE_RETRY)
  1168.         goto retry_src_close;
  1169.     if (temp_status == FILE_ABORT)
  1170.         return_status = temp_status;
  1171.     }
  1172.  
  1173.     return return_status;
  1174.  
  1175.  ret3:
  1176.     /* Remove short file */
  1177.     mc_unlink (dst_path);
  1178.     if (do_remote_copy) {
  1179.         mc_ctl (source_desc, MCCTL_FINISHREMOTE, -1);
  1180.         goto ret0;
  1181.     }
  1182.     goto ret;
  1183. }
  1184.  
  1185. /*
  1186.  * I think these copy_*_* functions should have a return type.
  1187.  * anyway, this function *must* have two directories as arguments.
  1188.  */
  1189. /* FIXME: This function needs to check the return values of the
  1190.    function calls */
  1191. int copy_dir_dir (char *s, char *d, int toplevel, int move_over)
  1192. {
  1193.     struct dirent *next;
  1194.     struct stat   buf, cbuf;
  1195.     DIR    *reading;
  1196.     char   *path, *mdpath, *dest_file, *dest_dir;
  1197.     int    return_status = FILE_CONT;
  1198.  
  1199.     /* First get the mode of the source dir */
  1200.  retry_src_stat:
  1201.     if (mc_lstat (s, &cbuf)){
  1202.     return_status = file_error (" Cannot stat source directory \"%s\" \n %s ", s);
  1203.     if (return_status == FILE_RETRY)
  1204.         goto retry_src_stat;
  1205.     return return_status;
  1206.     } 
  1207.  
  1208. /* FIXME: In this step we should do something
  1209.    in case the destination already exist */    
  1210.     /* Check the hardlinks */
  1211.     if (cbuf.st_nlink > 1 && check_hardlinks (s, d, &cbuf) == 1) {
  1212.         /* We have made a hardlink - no more processing is necessary */
  1213.         return return_status;
  1214.     }
  1215.  
  1216. /* Can someone answer: can cbuf be a link? How? */    
  1217.     if (S_ISLNK (cbuf.st_mode) && (!toplevel && dive_into_subdirs)){
  1218.         /* We want to replicate the symlink, not to copy the directory :-) */
  1219.         int result;
  1220.         
  1221.         if (!mc_stat (d, &buf) && toplevel && dive_into_subdirs){
  1222.             dest_dir = copy_strings (d, PATH_SEP_STR, x_basename (s), 0);
  1223.             result = copy_file_file (s, dest_dir, 1, 1);
  1224.             free (dest_dir);
  1225.             return result;
  1226.         } else
  1227.             return copy_file_file (s, d, 0, 1);
  1228.     }
  1229.     if (!S_ISDIR (cbuf.st_mode)){
  1230.     return_status = file_error (" Source directory \"%s\" is not a directory ", s);
  1231.     if (return_status == FILE_RETRY)
  1232.         goto retry_src_stat;
  1233.     return return_status;
  1234.     }
  1235.  
  1236.     /* Now, check if the dest dir exists, if not, create it. */
  1237.     if (mc_stat (d, &buf)){
  1238.         /* Here the dir doesn't exist : make it !*/
  1239.  
  1240.         if (move_over) {
  1241.             if (mc_rename (s, d) == 0)
  1242.         return FILE_CONT;
  1243.     }
  1244.     dest_dir = copy_strings (d, 0);
  1245.     } else {
  1246.         /*
  1247.          * If the destination directory exists, we want to copy the whole
  1248.          * directory, but we only want this to happen once.
  1249.      *
  1250.      * Escape sequences added to the * to avoid compiler warnings.
  1251.          * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
  1252.          * or ( /bla doesn't exist )       /tmp/\* to /bla     ->  /bla/\*
  1253.          */
  1254. /* Again, I'm getting curious. Is not d already what we wanted, incl.
  1255.    masked source basename? Is not this just a relict of the past versions? 
  1256.    I'm afraid this will lead into a two level deep dive :( */         
  1257.         if (toplevel && dive_into_subdirs){
  1258.             dest_dir = copy_strings (d, PATH_SEP_STR, x_basename (s), 0);
  1259.     } else {
  1260.         dest_dir = copy_strings (d, 0);
  1261.         goto dont_mkdir;
  1262.     }
  1263.     }
  1264.  retry_dst_mkdir:
  1265.     if (my_mkdir (dest_dir, cbuf.st_mode | S_IRWXU)){
  1266.     return_status = file_error (" Cannot create target directory \"%s\" \n %s ", dest_dir);
  1267.     if (return_status == FILE_RETRY)
  1268.         goto retry_dst_mkdir;
  1269.     return return_status;
  1270.     }
  1271.  
  1272.  dont_mkdir:
  1273.     /* open the source dir for reading */
  1274.     reading = mc_opendir (s);
  1275.  
  1276.     if (!reading){
  1277.     free (dest_dir);
  1278.     return return_status;
  1279.     }
  1280.     
  1281.     while ((next = mc_readdir (reading)) && return_status != FILE_ABORT){
  1282.         /*
  1283.          * Now, we don't want '.' and '..' to be created / copied at any time 
  1284.          */
  1285.         if (!strcmp (next->d_name, "."))
  1286.             continue;
  1287.         if (!strcmp (next->d_name, ".."))
  1288.            continue;
  1289.  
  1290.         /* get the filename and add it to the src directory */
  1291.         path = copy_strings (s, PATH_SEP_STR, next->d_name, 0);
  1292.         /* lstat is needed here, because we want even links to directories
  1293.            be replicated as links */
  1294.         mc_lstat (path, &buf);
  1295.  
  1296.         if (S_ISDIR (buf.st_mode)){
  1297.             mdpath = copy_strings (dest_dir, PATH_SEP_STR, next->d_name, 0);
  1298.             /*
  1299.              * From here, we just intend to recursively copy subdirs, not
  1300.              * the double functionality of copying different when the target
  1301.              * dir already exists. So, we give the recursive call the flag 0
  1302.              * meaning no toplevel.
  1303.              */
  1304.             return_status = copy_dir_dir (path, mdpath, 0, 0);
  1305.         free (mdpath);
  1306.     } else {
  1307.         dest_file = copy_strings (dest_dir, PATH_SEP_STR, x_basename (path), 0);
  1308.             return_status = copy_file_file (path, dest_file, 0, 1);
  1309.         free (dest_file);
  1310.     }
  1311.         free (path);
  1312.     }
  1313.     mc_closedir (reading);
  1314.     chmod (dest_dir, cbuf.st_mode);
  1315.     free (dest_dir);
  1316.     return return_status;
  1317. }
  1318.  
  1319. /* }}} */
  1320.  
  1321. /* {{{ Move routines */
  1322.  
  1323. static int move_file_file (char *s, char *d)
  1324. {
  1325.     struct stat src_stats, dst_stats;
  1326.     int return_status = FILE_CONT;
  1327.  
  1328.     if (show_source (s) == FILE_ABORT
  1329.     || show_target (d) == FILE_ABORT)
  1330.     return FILE_ABORT;
  1331.     
  1332.     refresh ();
  1333.  
  1334.  retry_src_lstat:
  1335.     if (mc_lstat (s, &src_stats) != 0){
  1336.     /* Source doesn't exist */
  1337.     return_status = file_error (" Cannot stat file \"%s\" \n %s ", s);
  1338.     if (return_status == FILE_RETRY)
  1339.         goto retry_src_lstat;
  1340.     return return_status;
  1341.     }
  1342.  
  1343.     if (mc_lstat (d, &dst_stats) == 0){
  1344.     /* Destination already exists */
  1345.     if (src_stats.st_dev == dst_stats.st_dev
  1346.         && src_stats.st_ino == dst_stats.st_ino){
  1347.         int msize = COLS - 36;
  1348.  
  1349.         if (msize < 0)
  1350.         msize = 40;
  1351.         msize /= 2;
  1352.         
  1353.         message (1, " Error ", " `%s' and `%s' are the same file ",
  1354.              name_trunc (s, msize), name_trunc (d, msize) );
  1355.         do_refresh ();
  1356.         return FILE_SKIP;
  1357.     }
  1358.     if (S_ISDIR (dst_stats.st_mode)){
  1359.         message (1, " Error ", " Cannot overwrite directory `%s' ", d);
  1360.         do_refresh ();
  1361.         return FILE_SKIP;
  1362.     }
  1363.  
  1364.     if (confirm_overwrite){
  1365.         return_status = query_replace (d, &src_stats, &dst_stats);
  1366.         if (return_status != FILE_CONT)
  1367.         return return_status;
  1368.     }
  1369.     /* Ok to overwrite */
  1370.     }
  1371.  
  1372.  retry_rename:
  1373.     if (!do_append) {
  1374.         if (mc_rename (s, d) == 0)
  1375.         return FILE_CONT;
  1376.     }
  1377. #if 0
  1378. /* Comparison to EXDEV seems not to work in nfs if you're moving from
  1379.    one nfs to the same, but on the server it is on two different
  1380.    filesystems. Then nfs returns EIO instead of EXDEV. 
  1381.    Hope it will not hurt if we always in case of error try to copy/delete. */
  1382.      else
  1383.         errno = EXDEV; /* Hack to copy (append) the file and then delete it */
  1384.  
  1385.     if (errno != EXDEV){
  1386.     return_status = files_error (" Cannot move file \"%s\" to \"%s\" \n %s ", s, d);
  1387.     if (return_status == FILE_RETRY)
  1388.         goto retry_rename;
  1389.     return return_status;
  1390.     }
  1391. #endif    
  1392.  
  1393.     /* Failed because filesystem boundary -> copy the file instead */
  1394.     if ((return_status = copy_file_file (s, d, 0, 0)) != FILE_CONT)
  1395.     return return_status;
  1396.     if ((return_status = show_source (NULL)) != FILE_CONT
  1397.     || (return_status = show_file_progress (0, 0)) != FILE_CONT)
  1398.     return return_status;
  1399.     
  1400.     refresh ();
  1401.  
  1402.  retry_src_remove:
  1403.     if (mc_unlink (s)){
  1404.     return_status = file_error (" Cannot remove file \"%s\" \n %s ", s);
  1405.     if (return_status == FILE_RETRY)
  1406.         goto retry_src_remove;
  1407.     return return_status;
  1408.     }
  1409.  
  1410.     return FILE_CONT;
  1411. }
  1412.  
  1413. int move_dir_dir (char *s, char *d)
  1414. {
  1415.     struct stat sbuf, dbuf, destbuf;
  1416.     char *destdir;
  1417.     int return_status;
  1418.     int move_over = 0;
  1419.  
  1420.     if (show_source (s) == FILE_ABORT
  1421.     || show_target (d) == FILE_ABORT)
  1422.     return FILE_ABORT;
  1423.     
  1424.     refresh ();
  1425.  
  1426.     mc_stat (s, &sbuf);
  1427.     if (mc_stat (d, &dbuf))
  1428.     destdir = copy_strings (d, 0);
  1429.     else if (!dive_into_subdirs) {
  1430.     destdir = copy_strings (d, 0);
  1431.     move_over = 1;
  1432.     } else
  1433. /* And I'm not sure about this as well - shouldn't we strdup always? */    
  1434.     destdir = copy_strings (d, PATH_SEP_STR, x_basename (s), 0);
  1435.  
  1436.     /* Check if the user inputted an existing dir */
  1437.  retry_dst_stat:
  1438.     if (!mc_stat (destdir, &destbuf)){
  1439.     if (move_over) {
  1440.         if ((return_status = copy_dir_dir (s, destdir, 0, 1)) != FILE_CONT)
  1441.         goto ret;
  1442.         goto oktoret;
  1443.     } else {
  1444.         if (S_ISDIR (destbuf.st_mode))
  1445.             return_status = file_error (" Cannot overwrite directory \"%s\" ", destdir);
  1446.         else
  1447.             return_status = file_error (" Cannot overwrite file \"%s\" ", destdir);
  1448.         if (return_status == FILE_RETRY)
  1449.             goto retry_dst_stat;
  1450.     }
  1451.         free (destdir);
  1452.         return return_status;
  1453.     }
  1454.  
  1455.  retry_rename:
  1456.     if (mc_rename (s, destdir) == 0){
  1457.     return_status = FILE_CONT;
  1458.     goto ret;
  1459.     }
  1460.  
  1461.     if (errno != EXDEV){
  1462.     return_status = files_error (" Cannot move directory \"%s\" to \"%s\" \n %s ", s, d);
  1463.     if (return_status == FILE_RETRY)
  1464.         goto retry_rename;
  1465.     goto ret;
  1466.     }
  1467.  
  1468.     /* Failed because of filesystem boundary -> copy dir instead */
  1469.     if ((return_status = copy_dir_dir (s, destdir, 0, 0)) != FILE_CONT)
  1470.     goto ret;
  1471. oktoret:
  1472.     if ((return_status = show_source (NULL)) != FILE_CONT
  1473.     || (return_status = show_file_progress (0, 0)) != FILE_CONT)
  1474.     goto ret;
  1475.     
  1476.     refresh ();
  1477.  
  1478.     return_status = recursive_erase (s);
  1479.  
  1480.  ret:
  1481.     free (destdir);
  1482.     return return_status;
  1483. }
  1484.  
  1485. /* }}} */
  1486.  
  1487. /* {{{ Erase routines */
  1488.  
  1489. static int erase_file (char *s)
  1490. {
  1491.     int return_status;
  1492.  
  1493.     if (show_deleting (s) == FILE_ABORT)
  1494.     return FILE_ABORT;
  1495.     
  1496.     refresh ();
  1497.  
  1498.  retry_unlink:
  1499.     if (mc_unlink (s)){
  1500.     return_status = file_error (" Cannot delete file \"%s\" \n %s ", s);
  1501.     if (return_status == FILE_RETRY)
  1502.         goto retry_unlink;
  1503.     return return_status;
  1504.     }
  1505.     return FILE_CONT;
  1506. }
  1507.  
  1508. static int recursive_erase (char *s)
  1509. {
  1510.     struct dirent *next;
  1511.     struct stat    buf;
  1512.     DIR    *reading;
  1513.     char   *path;
  1514.     int    return_status = FILE_CONT;
  1515.  
  1516.     if (!strcmp (s, ".."))
  1517.     return 1;
  1518.     
  1519.     reading = mc_opendir (s);
  1520.     
  1521.     if (!reading)
  1522.     return 1;
  1523.  
  1524.     while ((next = mc_readdir (reading)) && return_status == FILE_CONT){
  1525.     if (!strcmp (next->d_name, "."))
  1526.         continue;
  1527.        if (!strcmp (next->d_name, ".."))
  1528.         continue;
  1529.     path = copy_strings (s, PATH_SEP_STR, next->d_name, 0);
  1530.        if (mc_lstat (path, &buf)){
  1531.         free (path);
  1532.         return 1;
  1533.     } 
  1534.     if (S_ISDIR (buf.st_mode))
  1535.         return_status = (recursive_erase (path) != FILE_CONT);
  1536.     else
  1537.         return_status = erase_file (path);
  1538.     free (path);
  1539.     }
  1540.     mc_closedir (reading);
  1541.     if (return_status != FILE_CONT)
  1542.     return return_status;
  1543.     if (show_deleting (s) == FILE_ABORT)
  1544.     return FILE_ABORT;
  1545.     refresh ();
  1546.  retry_rmdir:
  1547.     if (my_rmdir (s)){
  1548.     return_status = file_error (" Cannot remove directory \"%s\" \n %s ", s);
  1549.     if (return_status == FILE_RETRY)
  1550.         goto retry_rmdir;
  1551.     return return_status;
  1552.     }
  1553.     return FILE_CONT;
  1554. }
  1555.  
  1556. int erase_dir (char *s)
  1557. {
  1558.     int error;
  1559.  
  1560.     if (strcmp (s, "..") == 0)
  1561.     return FILE_SKIP;
  1562.  
  1563.     if (strcmp (s, ".") == 0)
  1564.     return FILE_SKIP;
  1565.  
  1566.     if (show_deleting (s) == FILE_ABORT)
  1567.     return FILE_ABORT;
  1568.     refresh ();
  1569.  
  1570.  retry_rmdir:
  1571.     error = my_rmdir (s);
  1572.     if (error && (errno == ENOTEMPTY || errno == EEXIST)){
  1573.     error = query_recursive (s);
  1574.     if (error == FILE_CONT)
  1575.         return recursive_erase (s);
  1576.     else
  1577.         return error;
  1578.     }
  1579.     if (error){
  1580.     error = file_error (" Cannot remove directory \"%s\" \n %s ", s);
  1581.     if (error == FILE_RETRY)
  1582.         goto retry_rmdir;
  1583.     return error;
  1584.     }
  1585.     return FILE_CONT;
  1586. }
  1587.  
  1588. /* }}} */
  1589.  
  1590. /* {{{ Panel operate routines */
  1591.  
  1592. /* Returns currently selected file or the first marked file if there is one */
  1593. static char *get_file (struct stat *stat_buf)
  1594. {
  1595.     int i;
  1596.     WPanel *panel;
  1597.     
  1598.     if (get_current_type () == view_tree){
  1599.     WTree *tree = (WTree *)get_panel_widget (get_current_index ());
  1600.     
  1601.     mc_stat (tree->selected_ptr->name, stat_buf);
  1602.     return tree->selected_ptr->name;
  1603.     } 
  1604.  
  1605.     panel = cpanel;
  1606.     
  1607.     if (panel->marked){
  1608.     for (i = 0; i < panel->count; i++)
  1609.         if (panel->dir.list [i].f.marked){
  1610.         *stat_buf = panel->dir.list [i].buf;
  1611.         return panel->dir.list [i].fname;
  1612.         }
  1613.     } else {
  1614.     *stat_buf = panel->dir.list [panel->selected].buf;
  1615.     return panel->dir.list [panel->selected].fname;
  1616.     }
  1617.     fprintf (stderr, " Internal error: get_file \n");
  1618.     mi_getch ();
  1619.     return "";
  1620. }
  1621.  
  1622. static int is_wildcarded (char *p)
  1623. {
  1624.     for (; *p; p++) {
  1625.         if (*p == '*')
  1626.             return 1;
  1627.         else if (*p == '\\' && p [1] >= '1' && p [1] <= '9')
  1628.             return 1;
  1629.     }
  1630.     return 0;
  1631. }
  1632.  
  1633. #if BACKGROUND
  1634.  
  1635. int attention = 0;
  1636. int we_are_background = 0;
  1637.  
  1638. int
  1639. background_attention (int fd)
  1640. {
  1641.     char c;
  1642.     
  1643.     read (fd, &c, 1);
  1644.     attention = 1;
  1645. }
  1646.  
  1647. int background_processor (int fd)
  1648. {
  1649.     int c;
  1650.     char buffer [1024];
  1651.     
  1652.     while ((c = read (fd, buffer, sizeof (buffer))) > 0){
  1653.     if (attention)
  1654.         write (1, buffer, c);
  1655.     }
  1656. }
  1657.  
  1658. void
  1659. set_nonblocking (int fd)
  1660. {
  1661.     int val;
  1662.  
  1663.     val = fcntl (fd, F_GETFL, 0);
  1664.     val |= O_NONBLOCK;
  1665.     fcntl (fd, F_SETFL, val);
  1666. }
  1667.  
  1668. /* Try to make the Midnight Commander a background job */
  1669. /* return -1 on failure */
  1670. static int
  1671. do_background ()
  1672. {
  1673.     int fds [2];        /* stdin/stdout streams */
  1674.     int comm [2];        /* data connection stream */
  1675.     int pid;
  1676.     
  1677.     if (socketpair (AF_UNIX, SOCK_STREAM, 0, fd) == -1)
  1678.     return -1;
  1679.     if (socketpair (AF_UNIX, SOCK_STREAM, 0, comm) == -1){
  1680.     close (fd [0]);
  1681.     close (fd [1]);
  1682.     return -1;
  1683.     }
  1684.     
  1685.     if ((pid = fork ()) == -1)
  1686.     return -1;
  1687.     
  1688.     if (pid == 0){
  1689.     close (fd [0]);
  1690.     close (comm [0]);
  1691.     dup2 (fd [1], 0);    /* stdin */
  1692.     dup2 (fd [1], 1);    /* stdout */
  1693.     dup2 (fd [1], 2);    /* stderr */
  1694.     we_are_background = 1;
  1695.     } else {
  1696.     close (fd [1]);
  1697.     close (comm [1]);
  1698.     add_select_channel (fd [0], background_processor, 0);
  1699.     set_non_blocking (fd [0]);
  1700.     add_select_channel (comm [0], background_attention, 0);
  1701.     }
  1702. }
  1703. #endif
  1704.  
  1705. char *file_mask_dialog (char *header, char *text, char *def_text, int only_one)
  1706. {
  1707. #define FMDY 13
  1708.     int source_easy_patterns = easy_patterns;
  1709.     char *source_mask, *orig_mask, *dest_dir;
  1710.     const char *error;
  1711.     struct stat buf;
  1712.     int notroot, val;
  1713.     
  1714.     QuickDialog Quick_input;
  1715.     QuickWidget quick_widgets [] = {
  1716. /* preserve UID/GID must be the first one */    
  1717.     { quick_checkbox, 3, 64, 8, FMDY, "preserve UIDs/GIDs", 'U', 0, 0,
  1718.       &preserve_uidgid, 0, XV_WLAY_BELOWCLOSE, "t." },
  1719.     { quick_checkbox, 42, 64, 8, FMDY, "Stable Symlinks", 'S', 0, 0,
  1720.       &stable_symlinks, 0, XV_WLAY_BELOWCLOSE, "" },
  1721.     { quick_checkbox, 23, 64, 7, FMDY, "Dive into subdir if exists", 'D', 0, 0, 
  1722.       &dive_into_subdirs, 0, XV_WLAY_RIGHTOF, "" },
  1723.     { quick_checkbox, 3, 64, 7, FMDY, "follow sYmlinks", 'y', 0, 0, 
  1724.       &op_follow_symlinks, 0, XV_WLAY_BELOWOF, "" },
  1725. #ifdef HAVE_XVIEW
  1726. #define FMDI1 7
  1727. #define FMDI2 4
  1728. #define FMDC  6
  1729.     { quick_input, 3, 64, 6, FMDY, 0, 0, 58, 0, 
  1730.       0, 0, XV_WLAY_BELOWCLOSE, "" },
  1731. #endif
  1732.     { quick_label, 3, 64, 5, FMDY, "to:", 0, 0, 0, 0, 0, XV_WLAY_BELOWOF,"d."},
  1733.     { quick_checkbox, 37, 64, 4, FMDY, "using shell Patterns", 'p', 0, 0, 
  1734.       0/* &source_easy_patterns */, 0, XV_WLAY_BELOWCLOSE, "s." },
  1735.     { quick_input, 3, 64, 3, FMDY, 0 /* default text */, 0, 58, 
  1736.       0, 0, 0, XV_WLAY_BELOWCLOSE, "" },
  1737. #ifndef HAVE_XVIEW      
  1738. #define FMDI1 6
  1739. #define FMDI2 7
  1740. #define FMDC 5
  1741.     { quick_input, 3, 64, 6, FMDY, 0, 0, 58, 0, 
  1742.       0, 0, XV_WLAY_BELOWCLOSE },
  1743. #endif      
  1744. #define FMDI0 8      
  1745.     { quick_label, 2, 64, 2, FMDY, 0, 0, 0, 0, 0, 0, XV_WLAY_DONTCARE },
  1746. #define SKIP 2
  1747.     { quick_button, 34, 64, 9, FMDY, "[ Cancel ]", 'C', 2, B_CANCEL, 0, 0, 0,
  1748.       "b." },
  1749.     { quick_button, 20, 64, 9, FMDY, "[ Ok ]", 'O', 2, B_ENTER, 0, 0, 0, "" },
  1750. #if 0
  1751.     { quick_button, 48, 64, 9, FMDY, "[ & ]", '&', 2, B_USER, 0, 0, 0, 0, "" },
  1752. #endif
  1753.     { 0 } };
  1754.  
  1755.     stable_symlinks = 0;
  1756.     quick_widgets [FMDC].result = &source_easy_patterns;
  1757.     quick_widgets [FMDI1].text = easy_patterns ? "*" : "^\\(.*\\)$";
  1758.     notroot = (geteuid ()) ? 1 : 0;
  1759.     Quick_input.xlen  = 64;
  1760.     Quick_input.xpos  = -1;
  1761.     Quick_input.title = header;
  1762.     Quick_input.help  = "[Mask Copy/Rename]";
  1763.     Quick_input.class = "quick_file_mask";
  1764.     quick_widgets [FMDI0].text = text;
  1765.     quick_widgets [FMDI2].text = def_text;
  1766.     quick_widgets [FMDI2].str_result = &dest_dir;
  1767.     quick_widgets [FMDI1].str_result = &source_mask;
  1768.     Quick_input.ylen  = FMDY;
  1769.     Quick_input.widgets = quick_widgets + notroot;
  1770.     if (notroot) {
  1771.         int i;
  1772.         
  1773.         preserve_uidgid = 0;
  1774.     quick_widgets [1].tk_frame = "t.";
  1775.     }
  1776.  
  1777. ask_file_mask:
  1778.  
  1779.     if ((val = quick_dialog_skip (&Quick_input, SKIP)) == B_CANCEL)
  1780.     return 0;
  1781.  
  1782. #if BACKGROUND
  1783.     /* If it is a background operation */
  1784.     if (val == B_USER){
  1785.     if (do_background () != -1)
  1786.         return 0;
  1787.     /* If it did fail, continue as if no background support */
  1788.     }
  1789. #endif
  1790.     
  1791.     orig_mask = source_mask;
  1792.     if (!dest_dir || !*dest_dir) {
  1793.     if (source_mask)
  1794.         free (source_mask);
  1795.     return dest_dir;
  1796.     }
  1797.     if (source_easy_patterns) {
  1798.     source_easy_patterns = easy_patterns;
  1799.     easy_patterns = 1;
  1800.     source_mask = convert_pattern (source_mask, match_file, 1);
  1801.     source_easy_patterns = easy_patterns;
  1802.     }
  1803.     error = re_compile_pattern (source_mask, strlen (source_mask), &rx);
  1804.     if (error) {
  1805.     message (1, " Error ", "Invalid source pattern \"%s\" \n %s ",
  1806.          orig_mask, error);
  1807.     if (orig_mask)
  1808.         free (orig_mask);
  1809.     goto ask_file_mask;
  1810.     }
  1811.     if (orig_mask)
  1812.     free (orig_mask);
  1813.     dest_mask = strrchr (dest_dir, PATH_SEP);
  1814.     if (dest_mask == NULL)
  1815.     dest_mask = dest_dir;
  1816.     else
  1817.     dest_mask++;
  1818.     orig_mask = dest_mask;
  1819.     if (!*dest_mask || (!dive_into_subdirs && !is_wildcarded (dest_mask)) ||
  1820.     (dive_into_subdirs && ((!only_one && !is_wildcarded (dest_mask)) ||
  1821.                    (only_one && !mc_stat (dest_dir, &buf) && S_ISDIR (buf.st_mode)))))
  1822.     dest_mask = strdup ("*");
  1823.     else {
  1824.     dest_mask = strdup (dest_mask);
  1825.     *orig_mask = 0;
  1826.     }
  1827.     if (!*dest_dir) {
  1828.     free (dest_dir);
  1829.     dest_dir = strdup ("./");
  1830.     }
  1831.     return dest_dir;
  1832. }
  1833.  
  1834. /* Returns 1 if did change the directory structure,
  1835.    Returns 0 if user aborted */
  1836. int panel_operate (int operation, char *thedefault)
  1837. {
  1838. #ifdef WITH_FULL_PATHS
  1839.     char *source_with_path = NULL;
  1840. #else
  1841. #   define source_with_path source
  1842. #endif
  1843.     char *source = NULL;
  1844.     char *dest = NULL;
  1845.     char *temp = NULL;
  1846.     int only_one = (get_current_type () == view_tree) || (cpanel->marked <= 1);
  1847.     struct stat src_stat, dst_stat;
  1848.     int i, value;
  1849.     long marked, total;
  1850.     long count = 0, bytes = 0;
  1851.     int  dst_result;
  1852.  
  1853.     rx.buffer = NULL;
  1854.     if (linklist)
  1855.         free_linklist ();
  1856.     if (get_current_type () == view_listing)
  1857.     if (!cpanel->marked && !strcmp (selection (cpanel)->fname, "..")){
  1858.         message (1, " Error ", " Can't operate on \"..\"! ");
  1859.         return 0;
  1860.     }
  1861.     
  1862.     if (operation < OP_COPY || operation > OP_DELETE)
  1863.     return 0;
  1864.     
  1865.     /* Generate confirmation prompt */
  1866.     
  1867.     if (!only_one){
  1868.     sprintf (cmd_buf, "%s%d %s%s ", op_names [operation], cpanel->marked,
  1869.          (cpanel->marked == cpanel->dirs_marked) ? "directories" :
  1870.          (cpanel->dirs_marked) ? "files/directories" : "files",
  1871.          (operation == OP_DELETE) ? "?" : " with source mask:");
  1872.     } else {
  1873.     source = get_file (&src_stat);
  1874.     sprintf (cmd_buf,"%s%s \"%s\"%s ", op_names [operation],
  1875.              S_ISDIR (src_stat.st_mode) ? "directory" : "file",
  1876.          name_trunc (source, 28),
  1877.          (operation == OP_DELETE) ? "?" : " with source mask:");
  1878.     }
  1879.     
  1880.     /* Show confirmation dialog */
  1881.     if (operation == OP_DELETE && confirm_delete){
  1882.     i = query_dialog (op_names [operation], cmd_buf,
  1883.               3, 2, " Yes ", " No ");
  1884.     if (i != 0)
  1885.         return 0;
  1886.     } else if (operation != OP_DELETE) {
  1887.     char *dest_dir;
  1888.     
  1889.     if (thedefault != NULL)
  1890.         dest_dir = thedefault;
  1891.     else if (get_other_type () == view_listing)
  1892.         dest_dir = opanel->cwd;
  1893.     else
  1894.         dest_dir = cpanel->cwd;
  1895.  
  1896.     rx.buffer = (char *) xmalloc (MC_MAXPATHLEN, "mask copying");
  1897.     rx.allocated = MC_MAXPATHLEN;
  1898.     rx.translate = 0;
  1899.     dest = file_mask_dialog (op_names [operation],
  1900.                     cmd_buf, dest_dir, only_one);
  1901.     if (!dest) {
  1902.         free (rx.buffer);
  1903.         return 0;
  1904.     }
  1905.     if (!*dest){
  1906.         free (rx.buffer);
  1907.         free (dest);
  1908.         return 0;
  1909.     }
  1910.     }
  1911.  
  1912.     /* Initialize things */
  1913.     create_op_win (operation);
  1914.     ftpfs_hint_reread (0);
  1915.     
  1916.     /* Now, let's do the job */
  1917.     /* This code is only called by the tree and panel code */
  1918.     if (only_one){
  1919.     /* One file: FIXME mc_chdir will take user out of any vfs */
  1920.     if (operation != OP_COPY && get_current_type () == view_tree)
  1921.         mc_chdir (PATH_SEP_STR);
  1922.     
  1923.     /* The source and src_stat variables have been initialized before */
  1924. #ifdef WITH_FULL_PATHS
  1925.     source_with_path = copy_strings (cpanel->cwd, PATH_SEP_STR, source, 0);
  1926. #endif
  1927.     
  1928.     if (operation == OP_DELETE){
  1929.         /* Delete operation */
  1930.         if (S_ISDIR (src_stat.st_mode))
  1931.         value = erase_dir (source_with_path);
  1932.         else
  1933.         value = erase_file (source_with_path);
  1934.     } else {
  1935.         /* Copy or move operation */
  1936.         temp = transform_source (source_with_path);
  1937.         if (temp == NULL) {
  1938.         value = transform_error;
  1939.         } else {
  1940.         temp = get_full_name (dest, temp);
  1941.         free (dest);
  1942.         dest = temp;
  1943.         temp = 0;
  1944.             switch (operation){
  1945.             case OP_COPY:
  1946.             if (S_ISDIR (src_stat.st_mode))
  1947.                 value = copy_dir_dir (source_with_path, dest, 1, 0);
  1948.             else
  1949.                 value = copy_file_file (source_with_path, dest, 1, 1);
  1950.             break;
  1951.             case OP_MOVE:
  1952.             if (S_ISDIR (src_stat.st_mode))
  1953.                 value = move_dir_dir (source_with_path, dest);
  1954.             else
  1955.                 value = move_file_file (source_with_path, dest);
  1956.             break;
  1957.             default:
  1958.             value = FILE_CONT;
  1959.             message (1, " Internal failure ", " Unknown file operation ");
  1960.             }
  1961.         }
  1962.     } /* Copy or move operation */
  1963.  
  1964.     if (value == FILE_CONT)
  1965.         unmark_files (cpanel);
  1966.  
  1967.     } else {
  1968.     /* Many files */
  1969.  
  1970.     if (operation != OP_DELETE){
  1971.         /* Check destination for copy or move operation */
  1972.     retry_many_dst_stat:
  1973.         dst_result = mc_stat (dest, &dst_stat);
  1974.         if (dst_result == 0 && !S_ISDIR (dst_stat.st_mode)){
  1975.         if (file_error (" Destination \"%s\" must be a directory ", dest) == FILE_RETRY)
  1976.             goto retry_many_dst_stat;
  1977.         goto clean_up;
  1978.         }
  1979.     }
  1980.  
  1981.     /* Initialize variables for progress bars */
  1982.     marked = cpanel->marked;
  1983.     total = cpanel->total;
  1984.  
  1985.     /* Loop for every file */
  1986.     for (i = 0; i < cpanel->count; i++){
  1987.         if (!cpanel->dir.list [i].f.marked)
  1988.         continue;    /* Skip the unmarked ones */
  1989.         source = cpanel->dir.list [i].fname;
  1990.         src_stat = cpanel->dir.list [i].buf;    /* Inefficient, should we use pointers? */
  1991. #ifdef WITH_FULL_PATHS
  1992.         if (source_with_path)
  1993.             free (source_with_path);
  1994.         source_with_path = copy_strings (cpanel->cwd, PATH_SEP_STR, source, 0);
  1995. #endif
  1996.         if (operation == OP_DELETE){
  1997.         /* Delete operation */
  1998.         if (S_ISDIR (src_stat.st_mode))
  1999.             value = erase_dir (source_with_path);
  2000.         else
  2001.             value = erase_file (source_with_path);
  2002.         } else {
  2003.         /* Copy or move operation */
  2004.         if (temp)
  2005.             free (temp);
  2006.         temp = transform_source (source_with_path);
  2007.         if (temp == NULL) {
  2008.             value = transform_error;
  2009.         } else {
  2010.             temp = get_full_name (dest, temp);
  2011.             switch (operation){
  2012.             case OP_COPY:
  2013.                 if (S_ISDIR (src_stat.st_mode))
  2014.                 value = copy_dir_dir (source_with_path, temp, 1, 0);
  2015.                 else
  2016.                 value = copy_file_file (source_with_path, temp, 1, 1);
  2017.                 break;
  2018.             case OP_MOVE:
  2019.                 if (S_ISDIR (src_stat.st_mode))
  2020.                 value = move_dir_dir (source_with_path, temp);
  2021.                 else
  2022.                     value = move_file_file (source_with_path, temp);
  2023.                 break;
  2024.             default:
  2025.                 message (1, " Internal failure ",
  2026.                      " Unknown file operation ");
  2027.                 goto clean_up;
  2028.             }
  2029.         }
  2030.         } /* Copy or move operation */
  2031.  
  2032.         if (value == FILE_ABORT)
  2033.         goto clean_up;
  2034.         if (value == FILE_CONT){
  2035.         file_mark (cpanel, i, 0);
  2036.         cpanel->marked --;
  2037.         if (S_ISDIR (src_stat.st_mode))
  2038.             cpanel->dirs_marked --;
  2039.         cpanel->total -= src_stat.st_size;
  2040.         }
  2041.         count ++;
  2042.         if (show_count_progress (count, marked) == FILE_ABORT)
  2043.         goto clean_up;
  2044.         bytes += src_stat.st_size;
  2045.         if (verbose &&
  2046.             show_bytes_progress (bytes, total) == FILE_ABORT)
  2047.         goto clean_up;
  2048.         if (operation != OP_DELETE && verbose
  2049.         && show_file_progress (0, 0) == FILE_ABORT)
  2050.         goto clean_up;
  2051.         refresh ();
  2052.     } /* Loop for every file */
  2053.     } /* Many files */
  2054.  
  2055.  clean_up:
  2056.     /* Clean up */
  2057.     destroy_op_win ();
  2058.     ftpfs_hint_reread (1);
  2059.     if (linklist)
  2060.         free_linklist ();
  2061. #if WITH_FULL_PATHS
  2062.     if (source_with_path)
  2063.         free (source_with_path);
  2064. #endif
  2065.     if (dest)
  2066.     free (dest);
  2067.     if (temp)
  2068.     free (temp);
  2069.     if (rx.buffer) {
  2070.     free (rx.buffer);
  2071.     rx.buffer = NULL;
  2072.     }
  2073.     if (dest_mask) {
  2074.     free (dest_mask);
  2075.     dest_mask = NULL;
  2076.     }
  2077.     return 1;
  2078. }
  2079.  
  2080. /* }}} */
  2081.  
  2082. /*
  2083.   Cause emacs to enter folding mode for this file:
  2084.   Local variables:
  2085.   end:
  2086. */
  2087.